﻿using System;
using System.Windows.Input;

namespace LUCAStudios.Silverlight.Controls.Commanding
{
    /// <summary>
    /// Delegate used in CommandBinding(ICommand) operations
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="parameter"></param>
    /// <returns></returns>
    public delegate bool CanExecuteDelegate<in T>(T parameter);
    /// <summary>
    /// Delegate used in CommandBinding(ICommand) operations
    /// </summary>
    /// <returns></returns>
    public delegate bool CanExecuteDelegate();

    /// <summary>
    /// Helper class to generate command binders using lambda's
    /// </summary>
    public class CommandBinding : ICommand
    {
        private readonly Action _executionDelegate;
        private readonly CanExecuteDelegate _canExecuteDelegate;

        /// <summary>
        /// Constructs the command binding class, using the given Action delegate for the execution command.
        /// Using this constructor means the command will always be able to execute.
        /// </summary>
        /// <param name="executeCommand"></param>
        public CommandBinding(Action executeCommand)
        {
            _executionDelegate = executeCommand;
            _canExecuteDelegate = () => true;

        }
        /// <summary>
        /// Constructs the command binding class, using the given Action delegate, along with a CanExecuteDelegate
        /// </summary>
        /// <param name="executeCommand"></param>
        /// <param name="canExecuteDelegate"></param>
        public CommandBinding(Action executeCommand, CanExecuteDelegate canExecuteDelegate)
        {
            _executionDelegate = executeCommand;
            _canExecuteDelegate = canExecuteDelegate;
        }

        /// <summary>
        /// Fires the CanExecuteChanged event
        /// </summary>
        public void OnCanExecuteChanged()
        {
            if (CanExecuteChanged != null) CanExecuteChanged(this, new EventArgs());
        }

        #region ICommand Members

        /// <summary>
        /// returns a boolean from the CanExecuteDelegate
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public bool CanExecute(object parameter)
        {
            return _canExecuteDelegate.Invoke();
        }

        /// <summary>
        /// The CanExecuteChanged event, required by ICommand
        /// </summary>
        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// Invokes the ExecutionDelegate
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(object parameter)
        {
            _executionDelegate.Invoke();
        }

        #endregion
    }

    /// <summary>
    /// Helper class to generate command bindings using lamba's
    /// </summary>
    /// <typeparam name="T">Object type for bound command parameter</typeparam>
    public class CommandBinding<T> : ICommand
    {
        private readonly Action<T> _executionDelegate;
        private readonly CanExecuteDelegate<T> _canExecuteDelegate;

        /// <summary>
        /// Constructs the command binding class, using the given Action delegate for the execution command.
        /// Using this constructor means the command will always be able to execute.
        /// </summary>
        /// <param name="executeCommand"></param>
        public CommandBinding(Action<T> executeCommand)
        {
            _executionDelegate = executeCommand;
            _canExecuteDelegate = _ => true;

        }
        /// <summary>
        /// Constructs the command binding class, using the given Action delegate, along with a CanExecuteDelegate
        /// </summary>
        /// <param name="executeCommand"></param>
        /// <param name="canExecuteDelegate"></param>
        public CommandBinding(Action<T> executeCommand, CanExecuteDelegate<T> canExecuteDelegate)
        {
            _executionDelegate = executeCommand;
            _canExecuteDelegate = canExecuteDelegate;
        }

        /// <summary>
        /// Fires the CanExecuteChanged event
        /// </summary>
        public void OnCanExecuteChanged()
        {
            if (CanExecuteChanged != null) CanExecuteChanged(this, new EventArgs());
        }

        #region ICommand Members

        /// <summary>
        /// returns a boolean from the CanExecuteDelegate, which is passed the given parameter
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public bool CanExecute(object parameter)
        {
            return _canExecuteDelegate((T)parameter);
        }

        /// <summary>
        /// The CanExecuteChanged event, required by ICommand
        /// </summary>
        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// Invokes the ExecutionDelegate, passing the given parameter to it
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(object parameter)
        {
            _executionDelegate((T)parameter);
        }

        #endregion
    }
}
